package edu.unc.ils.nasa;

import java.io.File;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.xml.namespace.QName;

import maui.stemmers.PorterStemmer;
import maui.stemmers.Stemmer;

import org.apache.commons.collections15.Bag;
import org.apache.commons.collections15.bag.HashBag;
import org.apache.commons.io.FileUtils;
import org.openrdf.concepts.skos.core.Concept;
import org.openrdf.elmo.ElmoModule;
import org.openrdf.elmo.sesame.SesameManager;
import org.openrdf.elmo.sesame.SesameManagerFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.nativerdf.NativeStore;

public class KnowledgeBase 
{    
    Map<String, Bag<String>> kbMap = new TreeMap<String, Bag<String>>();
    Map<String, String> idPrefLabelMap = new HashMap<String, String>();
    List<String> stopwords = new ArrayList<String>();
    Stemmer stemmer = new PorterStemmer();
    
    public static void main(String[] args) throws Exception
    {
        KnowledgeBase kb = new KnowledgeBase();
        kb.loadStopwords("src/main/resources/stopwords_en.txt");
        kb.load("/Users/cwillis/dev/hive/hive-data/nalt/naltStoreEn");
        
        kb.test();
    }
    

    
    public void test()
    {
        String text = "Background: Cotton (Gossypium hirsutum L.) fibers are trichomes that initiate from the ovule epidermis. " + 
        "Little is known about the developmental pathway causing fiber to differentiate from ovular epidermal cells even " + 
        "though limits on the number of cells that differentiate into fiber will limit yield. Results: A method was developed " + 
        "to isolate RNA from fiber initials 1 day post anthesis (dpa). Complementary DNA libraries representing 1 dpa fibers " + 
        "and other cotton tissues were sequenced and analyzed. Assembly of G. hirsutum Expressed Sequenced Tags (ESTs) identified " + 
        "over 11,000 sequences not previously represented in GenBank. New genes identified among these ESTs were represented " + 
        "on microarrays. The microarrays were used to identify genes enriched in fiber initials (1 dpa fibers) and elongating " + 
        "fibers. Analyses of Gene Ontologies (GO) of differentially expressed genes determined that terms associated with the " + 
        "membranes were statistically over represented among genes increased in expression in fiber initials and 10 dpa fibers. " + 
        "Staining ovules with a fluorescent dye confirmed an increase in Endoplasmic Reticulum (ER) occurred in fiber initials on " + 
        "the day of anthesis, persisted through 3 dpa and was absent in a fiberless mutant. Two genes similar to the " + 
        "CAPRICE/TRIPTYCHON (CPC) gene that inhibits differentiation of leaf trichomes in Arabidopsis were also characterized. " + 
        "Genes associated with novel regulation of brassinosterols, GTP mediated signal transduction and cell cycle control and " +
        "components of a Ca+2 mediated signaling pathway were identified. Staining of cellular Ca+2 indicated that fiber initials " +
        "had more Ca+2 than other ovule cells supporting a role for Ca+2 in fiber development. Conclusion: Analysis of genes " + 
        "expressed in fiber initials identified a unique stage in fiber development characterized by an increase in ER and Ca+2 " + 
        "levels that occurred between 0 and 1 dpa. The gene similar to CPC has a MYB domain but appears to lack a transcription " + 
        "activating domain similar to the Arabisopsis gene. The method used to stain the ER also can be used to count fiber " + 
        "initials and showed fiber cells develop from adjacent cells unlike leaf trichomes.";
        
        String[] segments = text.split("\\. ");
        int n = 5;
        Bag<String> matched = new HashBag<String>();
        for (String segment: segments)
        {
            segment = segment.replaceAll(",", "");
            segment = segment.replaceAll(":", "");
            segment = segment.replaceAll("\\(", "");
            segment = segment.replaceAll("\\)", "");
            segment = segment.toLowerCase();
            
            segment = removeStopwords(segment);

            String[] words = segment.split(" ");
            for (int i=0; i<words.length; i++)
            {
                String key = stemmer.stem(words[i]);
                if (!stopwords.contains(words[i]))
                {
                    List<String> ids = lookup(words, key, i, i+n);
                    for (String id: ids)
                    {
                        matched.add(id);
                    }
                }
            }
        }
        for (String id: matched.uniqueSet())
        {
            String prefLabel = idPrefLabelMap.get(id);
            System.out.println(prefLabel + " " + matched.getCount(id));
        }
    }
    
    public String removeStopwords(String segment)
    {
        StringBuffer stopped = new StringBuffer();
        
        String[] words = segment.split(" ");
        
        for (String word: words) {
            if (!stopwords.contains(word))
            {
                if (stopped.length() > 0)
                    stopped.append(" ");
                stopped.append(word);
            }
                
        }
        return stopped.toString();
    }
    

    public List<String> lookup(String[] words, String prefix, int start, int length)
    {
        List<String> found = new ArrayList<String>();
        
        Bag<String> ids = kbMap.get(prefix);
        if (ids != null)
        {
            for (String id: ids)
                found.add(id);
        }
       
        for (int i=start+1; i<length; i++)
        {
            if (i > words.length-1)
                continue;
            
            if (!stopwords.contains(words[i]))
            {
                String key = prefix + ";" + stemmer.stem(words[i]);            
                List<String> f = lookup(words,key, i,length);
                found.addAll(f);
            }
        }
        return found;
    }
    
    // NASA uses a separate phrase-matching file. For now, we'll just use the vocabulary
    // as the source for the KB.
    public void load(String path) throws Exception
    {
        // Open the Sesame repository
        String indexes = "spoc,ospc";
        NativeStore store = new NativeStore(new File(path), indexes);
        Repository repository = new SailRepository(store);
        repository.initialize();
        ElmoModule module = new ElmoModule();
        SesameManagerFactory factory = new SesameManagerFactory(module, repository);
        SesameManager manager = factory.createElmoManager(); 
        
        for (Concept concept:  manager.findAll(Concept.class))
        {
            String prefLabel = concept.getSkosPrefLabel();
            Set<String> altLabels = concept.getSkosAltLabels();
            QName qname = concept.getQName();
            String uri = qname.getNamespaceURI() + qname.getLocalPart();
            idPrefLabelMap.put(uri,prefLabel);

            addPhrase(prefLabel, uri);
            for (String altLabel: altLabels)
                addPhrase(altLabel, uri);
        }
        
        repository.shutDown();
        store.shutDown();
    }
    
    public void loadStopwords(String path) throws Exception
    {
        stopwords = FileUtils.readLines(new File(path), "UTF-8");
    }
    public void addPhrase(String phrase, String uri)
    {
        
        boolean isProper = Character.isUpperCase(phrase.charAt(0));
        

        // Handle qualified names and expanded acronyms
        if (phrase.contains(" ("))
        {
            phrase = phrase.replaceAll(" \\(", "");
            phrase = phrase.replaceAll("\\)", "");
        }
            
        // Flip inverted headings
        phrase = flip(phrase);
        
        List<String> keys = createKeys(phrase);
        for (String key: keys)
        {
            Bag<String> ids = kbMap.get(key);
            if (ids == null)
                ids = new HashBag<String>();
            ids.add(uri);
            
            kbMap.put(key.toLowerCase(), ids);
        }
    }
    
    public List<String> createKeys(String phrase)
    {
        List<String> keys = new ArrayList<String>();
        
        String[] words = phrase.split(" ");
        String key = "";


        for (String word: words)
        {
            word = word.replaceAll(", ", " ");
            word = word.replaceAll("'", "");
            if (word.endsWith("."))
                word = word.substring(0, word.length() - 1);
            
            if (!stopwords.contains(word))
            {
                word = stemmer.stem(word);
                key += ((key.length()>0)?";":"") + word;
            }
        }
        keys.add(key);
        
        if (phrase.contains("-"))
        {
            phrase = phrase.replaceAll("-", " ");
            keys.addAll(createKeys(phrase));
        }

        return keys;
    }
    
    public String flip(String invertedPhrase)
    {
        int commaPos = invertedPhrase.indexOf(", ");
        if (commaPos > -1 && commaPos == invertedPhrase.lastIndexOf(", "))
        {
            String[] tmp = invertedPhrase.split(", ");
            invertedPhrase = tmp[1] + " " + tmp[0];
        }
        return invertedPhrase;
    }
}
